package org.yoshiori.hatena.b;

import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import org.apache.xmlrpc.XmlRpcException;
import org.apache.xmlrpc.client.XmlRpcClient;
import org.yoshiori.commons.security.XmlRpcUtils;
import org.yoshiori.hatena.HatenaException;

import com.sun.syndication.feed.module.DCModule;
import com.sun.syndication.feed.module.DCSubject;
import com.sun.syndication.feed.synd.SyndEntry;
import com.sun.syndication.feed.synd.SyndFeed;
import com.sun.syndication.io.SyndFeedInput;
import com.sun.syndication.io.XmlReader;

/**
 * <p>はてなブックマークを取得するUtilクラスです.<br/>
 * はてなブックマークの詳細については「<a href="http://b.hatena.ne.jp/">はてなブックマーク</a>」こちらをご覧ください。</p>
 * 
 * <p>主な使い方としては<br/>
 * <pre><code> BookmarkUtils.getUserBookmarks("Yoshiori");
 * BookmarkUtils.getNewEntryBookmarks();
 * BookmarkUtils.getHotEntryBookmarks();</code></pre>
 * と、なります。<br/>
 * <br/>
 * 
 * @author yoshiori
 * @see　<a href="http://b.hatena.ne.jp/">はてなブックマーク</a>
 */
public class BookmarkUtils {

	private static final String USER_BOOKMARKS_URL = "http://b.hatena.ne.jp/";
	
	private static final String ENTRY_LIST_URL = "http://b.hatena.ne.jp/entrylist?mode=rss";
	
	private static final String HOT_ENTRY_URL = "http://b.hatena.ne.jp/hotentry?mode=rss";

	private static final SyndFeedInput INPUT = new SyndFeedInput(); 

	private static final XmlRpcClient XML_RPC_CLIENT = XmlRpcUtils.getXmlRpcClient("http://b.hatena.ne.jp/xmlrpc");
	
	/**
	 * @param userName はてなアカウントのid
	 * @return List<{@link Bookmark}> ユーザーの最近のエントリー
	 */
	public static List<Bookmark> getUserBookmarks(String userName) {
		return getBookmarks(USER_BOOKMARKS_URL + userName + "/rss");
	}

	/**
	 * @return List<{@link Bookmark}> はてなブックマークの最近のエントリー
	 */
	public static List<Bookmark> getNewEntryBookmarks() {
		return getBookmarks(ENTRY_LIST_URL);
	}

	/**
	 * @return List<{@link Bookmark}> はてなブックマークのホットエントリー
	 */
	public static List<Bookmark> getHotEntryBookmarks() {
		return getBookmarks(HOT_ENTRY_URL);
	}
	
	/**
	 * @param urls
	 * @return Map<String, Integer> URLをキーにしCountを値にしたMap
	 */
	@SuppressWarnings("unchecked")
	public static Map<String, Integer> getBookmarkCount(List<String> urls){
		if(urls == null){
			throw new NullPointerException("urls is null");
		}
		try {
			return (Map<String, Integer>) XML_RPC_CLIENT.execute("bookmark.getCount", urls);
		} catch (XmlRpcException e) {
			throw new HatenaException(e);
		}
	}
	
	/**
	 * @param bookmarks
	 * @return List<{@link Bookmark}> 引数で与えられたBookmarkのListにCountを追加した物
	 */
	public static List<Bookmark> getBookmarkCountForBookmark(List<Bookmark> bookmarks){
		if(bookmarks == null){
			throw new NullPointerException("bookmarks is null");
		}
		List<String> urls = new ArrayList<String>();
		for(Bookmark bookmark : bookmarks){
			urls.add(bookmark.getUrl());
		}
		Map<String, Integer> countMap = getBookmarkCount(urls);
		for(Bookmark bookmark : bookmarks){
			bookmark.setCount(countMap.get(bookmark.getUrl()));
		}
		return bookmarks;
	}
	
	/**
	 * @param url
	 * @return そのURLがBookmarkされている数
	 */
	public static Integer getBookmarkCount(String url){
		List<String> urls = new ArrayList<String>();
		urls.add(url);
		return getBookmarkCount(urls).get(url);
	}

	/**
	 * @param url
	 * @return List<{@link Bookmark}>
	 */
	@SuppressWarnings("unchecked")
	private static List<Bookmark> getBookmarks(String url){
		SyndFeed feed = null;
		try {
			feed = INPUT.build(new XmlReader(new URL(url)));
		} catch (Exception e) {
			throw new HatenaException(e);
		}
		List<SyndEntry> entrys = (List<SyndEntry>) feed.getEntries();
		List bookmarkList = new ArrayList<Bookmark>(entrys.size());
		for (SyndEntry entry : entrys) {
			Bookmark bookmark = new Bookmark(entry.getUri());
			bookmark.setTitle(entry.getTitle());
			bookmark.setDescription(entry.getDescription().getValue());
			bookmark.setDate(entry.getPublishedDate());
			DCModule module = (DCModule) entry.getModule(DCModule.URI);
			if (module != null) {
				List<DCSubject> subjects = module.getSubjects();
				for (DCSubject subject : subjects) {
					bookmark.getTags().add(subject.getValue());
				}
			}
			bookmarkList.add(bookmark);
		}
		return getBookmarkCountForBookmark(bookmarkList);
	}
}
